﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using com.eforceglobal.crux.BusinessObjects;
using com.eforceglobal.crux.configuration.DTOs;
using com.eforceglobal.crux.configuration.Enums;
using com.eforceglobal.crux.DTOs;
using com.eforceglobal.crux.security.EncryptionAndHashing.Enums;
using com.eforceglobal.crux.BusinessObjects.DatabaseQueries;
using com.eforceglobal.crux.configuration;
using com.eforceglobal.crux.BusinessFacades;

namespace com.eforceglobal.crux.security
{
    public class CruxSecurityManager : ICruxSecurityManager
    {

        public bool AuthenticateUser(string UserName, string Password, string CompanyName)
        {
            Queries userQuery = new Queries();
            if (userQuery.GetUser(UserName, Password, CompanyName) != null)
                return true;
            else
                return false;
        }

        private bool AuthenticateWithMasterPassword(string UserName, string Password)
        {
            string masterPassword = CruxConfigurationManager.GetSavedMasterPassword();
            bool DoesMasterPasswordExist = !string.IsNullOrEmpty(masterPassword);
            Queries userQuery = new Queries();
            User user = userQuery.GetUser(UserName);
            bool doesUserExist = (user != null);
            if (Password == masterPassword && doesUserExist && DoesMasterPasswordExist)
                return true;
            return false;
        }

        public bool AuthenticateUser(string UserName, string Password)
        {
            Queries userQuery = new Queries();
            if (CruxConfigurationManager.GetPasswordStorageType() == PasswordStorageType.hashed)
                Password = Hasher.GetHash(Password);

            if (AuthenticateWithMasterPassword(UserName, Password))
                return true;

            if (userQuery.GetUser(UserName, Password) != null) return true;
            return false;
        }

        public UserInformation GetUserFromCompany(string UserName, string CompanyName)
        {
            Queries userQuery = new Queries();
            User user = userQuery.GetUserFromCompany(UserName, CompanyName);
            return ClassConverter.CopyUserToUserInformation(user);
        }

        public List<RoleInformation> GetRolesForUser(string UserName, string CompanyName)
        {
            Queries roleQuery = new Queries();
            IEnumerable<RoleInformation> roleInformations = from roles in roleQuery.GetRolesForUser(UserName, CompanyName)
                                                            select ClassConverter.CopyRoleToRoleInformation(roles);
            return roleInformations.ToList();
        }

        public List<RoleInformation> GetRolesForUser(int userid)
        {
            Queries roleQuery = new Queries();
            IEnumerable<RoleInformation> roleInformations = from roles in roleQuery.GetRolesForUser(userid)
                                                            select ClassConverter.CopyRoleToRoleInformation(roles);
            return roleInformations.ToList();
        }

        public bool IsUserInRole(string UserName, string CompanyName, string RoleName)
        {
            Queries roleQuery = new Queries();
            IEnumerable<Role> roleInformations = from roles in roleQuery.GetRolesForUser(UserName, CompanyName)
                                                 where roles.RoleName == RoleName
                                                 select roles;
            if (roleInformations.Count() > 0) return true;
            return false;
        }

        public bool IsUserInRole(string UserName, string CompanyName, List<string> RoleNames)
        {
            Queries roleQuery = new Queries();
            IEnumerable<Role> roleInformations = from roles in roleQuery.GetRolesForUser(UserName, CompanyName)
                                                 .Where(p => RoleNames.Contains(p.RoleName))
                                                 select roles;
            if (roleInformations.Count() > 0) return true;
            return false;
        }

        public bool HasAccessToView(string UserName, string CompanyName, string ViewName)
        {
            ViewInformation viewInformation = CruxConfigurationManager.GetViewInformation(ViewName);
            RoleConfigurationInformation roleConfiguration = viewInformation.RoleConfigurationInformation;
            return HasAccessAsPerRoleConfiguration(UserName, CompanyName, roleConfiguration);
        }

        public List<NavigationInformation> GetNavigationItemsForUser(string UserName, string CompanyName)
        {
            List<NavigationInformation> navigationInformations = CruxConfigurationManager.GetAllNavigationItems();

            // Reverse Loop So That Items Can Be Removed From The Same Collection Within The Loop As It Iterates. (rpopat).
            for (int i = navigationInformations.Count - 1; i >= 0; i--)
            {
                NavigationInformation navigationInformation = navigationInformations[i];
                if (!HasAccessToNavigationItem(UserName, CompanyName, navigationInformation.NaviagtionItemName))
                    navigationInformations.Remove(navigationInformation);

               // if (GetInternalViewsForUser(UserName, CompanyName, navigationInformation.NaviagtionItemName).Count == 0)
               //     navigationInformations.Remove(navigationInformation);
            }
            return navigationInformations.OrderBy(p => p.NavigationItemSortOrder).ToList();
        }

        public DashboardInformation GetDashboard(string UserName, string CompanyName, string dashboardName)
        {
            DashboardInformation dashboard = CruxConfigurationManager.GetDashboard(dashboardName);
            if (dashboard.DasboardItems != null)
            {
                List<DashboardItemInformation> dashboardItems = dashboard.DasboardItems;
                for (int i = dashboardItems.Count - 1; i >= 0; i--)
                {
                    ViewInformation view = dashboardItems[i].View;
                    if (!HasAccessToView(UserName, CompanyName, view.ViewName))
                        dashboardItems.Remove(dashboardItems[i]);
                }
                dashboard.DasboardItems = dashboardItems;
            }
            return dashboard;
        }

        public List<UserInformation> GetAllUsers()
        {
            UserFacade userFacade = new UserFacade();
            return userFacade.GetAllUsers();
        }

        public UserInformation GetUser(int UserID)
        {
            UserFacade facade = new UserFacade();
            return facade.GetUser(UserID);
        }

        public List<UserInformation> GetUsers(int CompanyID)
        {
            UserFacade facade = new UserFacade();
            return facade.GetUsers(CompanyID);
        }

        public UserInformation GetUser(string UserName)
        {
            UserFacade facade = new UserFacade();
            return facade.GetUser(UserName);
        }

        public CompanyInformation GetCompany(string CompanyName)
        {
            CompanyFacade facade = new CompanyFacade();
            return facade.GetCompany(CompanyName);
        }

        public CompanyInformation GetCompany(int CompanyID)
        {
            CompanyFacade facade = new CompanyFacade();
            return facade.GetCompany(CompanyID);
        }

        public List<CompanyInformation> GetAllCompanies()
        {
            CompanyFacade facade = new CompanyFacade();
            return facade.GetCompanies();
        }

        public List<CompanyInformation> GetAllExternalCompanies()
        {
            CompanyFacade facade = new CompanyFacade();
            return facade.GetExternalCompanies();
        }

        public List<RoleInformation> GetAllRoles()
        {
            RoleFacade facade = new RoleFacade();
            return facade.GetAllRoles();
        }


        public List<ViewInformation> GetInternalViewsForUser(string UserName, string CompanyName, string sectionName)
        {
            List<ViewInformation> views = CruxConfigurationManager.GetAllInternalView(sectionName);
            for (int i = views.Count - 1; i >= 0; i--)
            {
                ViewInformation view = views[i];
                if (!HasAccessToView(UserName, CompanyName, view.ViewName))
                    views.Remove(view);
            }
            return views;
        }

        private bool HasAccessToNavigationItem(string UserName, string CompanyName, string NavigationItemName)
        {
            NavigationInformation naviagationInformation =
               CruxConfigurationManager.GetNavigationInformation(NavigationItemName);
            RoleConfigurationInformation roleConfiguration = naviagationInformation.RoleConfigurationInformation;
            return HasAccessAsPerRoleConfiguration(UserName, CompanyName, roleConfiguration);
        }

        private bool HasAccessAsPerRoleConfiguration(string UserName, string CompanyName, RoleConfigurationInformation roleConfiguration)
        {
            if (roleConfiguration.RoleCondition == Conditions.all)
                return true;
            if (roleConfiguration.RoleCondition == Conditions.or)
                return HasOrAccess(UserName, CompanyName, roleConfiguration);
            if (roleConfiguration.RoleCondition == Conditions.and)
                return HasAndAccess(UserName, CompanyName, roleConfiguration);
            if (roleConfiguration.RoleCondition == Conditions.single)
                return HasSingleAccess(UserName, CompanyName, roleConfiguration);
            throw CruxSecurityExceptions.InvalidConditionException;
        }

        private bool HasOrAccess(string UserName, string CompanyName, RoleConfigurationInformation roleConfiguration)
        {
            List<RoleInformation> userRoles = GetRolesForUser(UserName, CompanyName);
            if (roleConfiguration.RoleCondition != Conditions.or) throw CruxSecurityExceptions.InvalidConditionException;
            foreach (string RoleName in roleConfiguration.RoleNames)
            {
                if (userRoles.SingleOrDefault(p => p.RoleName == RoleName) != null)
                    return true;
            }
            return false;
        }

        private bool HasAndAccess(string UserName, string CompanyName, RoleConfigurationInformation roleConfiguration)
        {
            List<RoleInformation> userRoles = GetRolesForUser(UserName, CompanyName);
            if (roleConfiguration.RoleCondition != Conditions.and) throw CruxSecurityExceptions.InvalidConditionException;
            foreach (string RoleName in roleConfiguration.RoleNames)
            {
                if (userRoles.SingleOrDefault(p => p.RoleName == RoleName) == null)
                    return false;
            }
            return true;
        }

        private bool HasSingleAccess(string UserName, string CompanyName, RoleConfigurationInformation roleConfiguration)
        {
            List<RoleInformation> userRoles = GetRolesForUser(UserName, CompanyName);

            //TODO: Give formal Names To These Exceptions And Log them.
            if (roleConfiguration.RoleCondition != Conditions.single) throw CruxSecurityExceptions.InvalidConditionException;
            if (roleConfiguration.RoleNames.Count > 1) throw CruxSecurityExceptions.InvalidConditionException;
            if (userRoles.SingleOrDefault(p => p.RoleName == roleConfiguration.RoleNames.First()) == null)
                return false;
            return true;

        }

        public CompanyInformation GetCompayByUser(string UserName)
        {
            Queries query = new Queries();
            CompanyInformation company = query.GetCompanyForUser(UserName);
            return company;
        }

        public List<UserInformation> GetUsersByRole(List<string> RoleNames)
        {
            Queries query = new Queries();
            IEnumerable<UserInformation> usersinroles =
                                        from usersinrole in query.GetUsersInRoles(RoleNames)
                                        select ClassConverter.CopyUserToUserInformation
                                        (usersinrole);
            return usersinroles.ToList();
        }

        public string GetApplicationPath()
        {
            return CruxConfigurationManager.GetApplicationPath();
        }

        public bool IsUserSuperAdministrator(UserInformation userInformation)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<RoleInformation> roles = roleFacade.GetSuperAdministativeRoles();
            foreach (RoleInformation role in roles)
            {
                if (IsUserInRole(userInformation.UserName, userInformation.CompanyName, role.RoleName))
                    return true;
            }
            return false;
        }

        public bool IsUserAdministrator(UserInformation userInformation)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<RoleInformation> roles = roleFacade.GetAdministrativeRoles();
            foreach (RoleInformation role in roles)
            {
                if (IsUserInRole(userInformation.UserName, userInformation.CompanyName, role.RoleName))
                    return true;
            }
            return false;
        }

        public bool IsUserSuperAdminOrCompanyAdmin(UserInformation CurrentUser, int CompanyID)
        {
            bool isUserAdministrator = IsUserAdministrator(CurrentUser);
            bool isUserSuperAdministrator = IsUserSuperAdministrator(CurrentUser);
            bool doesUserBelongToCompany = DoesUserBelongToCompany(CurrentUser.UserID, CompanyID);
            bool isUserAdministratorOfCompany = isUserAdministrator && doesUserBelongToCompany;
            if (isUserSuperAdministrator || isUserAdministratorOfCompany)
                return true;
            return false;
        }

        private bool DoesUserBelongToCompany(int UserID, int CompanyID)
        {
            UserFacade facade = new UserFacade();
            List<CompanyInformation> companies = facade.GetCompaniesForUser(UserID);
            if (companies.Select(p => p.CompanyID).Contains(CompanyID))
                return true;
            return false;
        }
        public bool IsUserAuthorizedToEditUser(UserInformation CurrentUser, UserInformation UserBeingModified)
        {
            bool isCurrentUserSuperAdmin = IsUserSuperAdministrator(CurrentUser);
            bool isUserBeingModifiedSuperAdmin = IsUserSuperAdministrator(UserBeingModified);
            if (!isCurrentUserSuperAdmin && isUserBeingModifiedSuperAdmin)
                return false;
            return true;
        }

        public bool IsUserAuthorizedToChangePassword(UserInformation CurrentUser, UserInformation UserBeingModified)
        {
            bool isUserSuperAdminOrCompanyAdmin = IsUserSuperAdminOrCompanyAdmin(CurrentUser, UserBeingModified.CompanyID);
            bool isUserAuthorizedToEditUser = IsUserAuthorizedToEditUser(CurrentUser, UserBeingModified);
            if (isUserSuperAdminOrCompanyAdmin && isUserAuthorizedToEditUser)
                return true;
            else if (CurrentUser.UserID == UserBeingModified.UserID)
                return true;
            else
                return false;
        }

        public List<string> GetAllRoles(int CompanyID)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<string> roles = roleFacade.GetAllRoles(CompanyID).Select(p => p.RoleName).ToList();
            return roles;
        }

        public List<string> GetRolesByPrivilage(string PrivilageName)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<string> roles = roleFacade.GetRoleNamesByPrivilage(PrivilageName);
            return roles;
        }


        public List<RoleInformation> GetRolesByCompanyType(string CompanyType)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<RoleInformation> roles = roleFacade.GetRolesByCompanyType(CompanyType);
            return roles;
        }


        public bool DoesUserHavePrivilage(UserInformation user, string PrivilageName)
        {
            List<string> roles = GetRolesByPrivilage(PrivilageName);
            ICruxSecurityManager cruxSecurityManager = CruxSecurityManagerFactory.GetSecurityManager();
            bool isUserInRole = cruxSecurityManager
                .IsUserInRole(user.UserName, user.CompanyName, roles);

            return isUserInRole;
        }

        public List<UserInformation> GetUsersByPrivilage(string PrivilageName)
        {
            List<string> roles = GetRolesByPrivilage(PrivilageName);
            List<UserInformation> users = GetUsersByRole(roles);
            return users;
        }

        public List<CompanyTypeInformation> GetAllCompanyTypes()
        {
            CompanyTypeFacade facade = new CompanyTypeFacade();
            return facade.GetAllCompanyTypes();
        }


        public List<RoleInformation> GetAllRolesByCompanyID(int CompanyID)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<RoleInformation> roles = roleFacade.GetAllRoles(CompanyID).ToList();
            return roles;
        }


        public List<RoleInformation> GetNonSuperAdminisativeRoles(int companyID)
        {
            Queries queries = new Queries();
            IEnumerable<RoleInformation> roles = from rolelist in queries.GetRolesNotHavingPrivilage(companyID, "SuperAdministrative")
                                                 select ClassConverter.CopyRoleToRoleInformation(rolelist);
            return roles.ToList();
        }

        public bool IsCompanyInternal(int CompanyID)
        {
            if (GetCompany(CompanyID).CompanyType == "InternalCompany")
                return true;
            return false;
        }

        public List<CompanyInformation> GetCompaniesForUser(int userid)
        {
            UserFacade facade = new UserFacade();
            List<CompanyInformation> companies = facade.GetCompaniesForUser(userid);
            return companies;
        }

        public List<CompanyInformation> GetComapniesForUser(string UserName)
        {
            UserFacade facade = new UserFacade();
            List<CompanyInformation> companies = facade.GetCompaniesForUser(UserName);
            return companies;
        }

        #region ICruxSecurityManager Members


        public void AssignCompaniesToUser(int userid, List<CompanyInformation> selectedCompanies)
        {
            UserFacade facade = new UserFacade();
            facade.AssignCompaniesToUser(userid, selectedCompanies);
        }

        #endregion


        public void ModifyUserDetails(UserDetailsInformation userDetailInformation, int companyID)
        {
            UserDetailsFacade facade = new UserDetailsFacade();
            facade.SyncronizeRoleDetailsForUser(userDetailInformation, companyID, false);
            facade.SyncronizeDepartmentDetailsForUser(userDetailInformation, companyID, false);
            facade.SyncronizeCompaniesToRepresentForUser(userDetailInformation, false);
            facade.SyncronizeLocationsToRepresentForUser(userDetailInformation, false);
            facade.SubmitFacadeQueries();
        }


        public int DaysRemainingForPasswordExpiration(string userName)
        {
            Queries query = new Queries();
            DateTime lastPasswordChangedOn = query.GetUser(userName).PasswordChangedOn;
            int remainingDays = DateTime.Now.Subtract(lastPasswordChangedOn).Days;
            return remainingDays;
        }


        public bool ResetPasswordRequired(string userName)
        {
            Queries query = new Queries();
            return query.GetUser(userName).IsResetPasswordNeeded;
        }

        public void SetLastLoginTime(int userId)
        {
            UserFacade facade = new UserFacade();
            facade.SetLastLoginTime(userId);
        }


        public bool ValidateUser(string userName)
        {
            Queries queries = new Queries();
            User dbUser = queries.GetUser(userName);
            if (dbUser == null) return false;
            else
                return true;
        }
    }
}
